/*
 * Copyright (c) Ulrich Hilger, http://dev.uhilger.de, all rights reserved.
 *
 * Published under the terms and conditions of the Apache License 2.0,
 * see http://www.apache.org/licenses/LICENSE-2.0
 */

package de.uhilger.servicehub.view;

import de.uhilger.app.ui.GridBagHelper;
import de.uhilger.servicehub.ServiceHub;
import de.uhilger.servicehub.model.Role;
import de.uhilger.servicehub.model.User;
import de.uhilger.servicehub.model.UserStore;
import de.uhilger.servicehub.server.ServiceHubUser;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ResourceBundle;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

/**
 * A panel to edit user data for application ServiceHub
 *
 * @author Ulrich Hilger, <a href="http://dev.uhilger.de" target="_blank">http://dev.uhilger.de</a>
 * @author Published under the terms and conditions of
 * the <a href="http://www.apache.org/licenses/LICENSE-2.0" target="_blank">Apache License 2.0</a>
 */
public class UserPanel extends JPanel implements ActionListener, ListSelectionListener, ChangeListener {

  private static final String RN_USER_PANEL_NEW_BUTTON_LABEL = "userPanelNewButtonLabel";
  private static final String RN_USER_PANEL_DELETE_BUTTON_LABEL = "userPanelDeleteButtonLabel";
  private static final String RN_USER_PANEL_CANCEL_BUTTON_LABEL = "userPanelCancelButtonLabel";
  private static final String RN_USER_PANEL_ADD_BUTTON_LABEL = "userPanelAddButtonLabel";
  private static final String RN_USER_PANEL_SAVE_BUTTON_LABEL = "userPanelSaveButtonLabel";
  private static final String RN_USER_PANEL_USER_NAME_LABEL = "userPanelUserNameLabel";
  private static final String RN_USER_PANEL_USER_PASSWORD_LABEL = "userPanelUserPasswordLabel";
  private static final String RN_USER_PANEL_REMOVE_BUTTON_LABEL = "userPanelRemoveButtonLabel";
  private static final String RN_USER_PANEL_USER_ROLE_LIST_LABEL = "userPanelUserRoleListLabel";
  private static final String RN_USER_PANEL_ROLE_LIST_LABEL = "userPanelRoleListLabel";

  private JList userList;
  private JList userRoleList;
  private JList roleList;
  private JButton newUserBtn;
  private JButton delUserBtn;
  private JButton addRoleBtn;
  private JButton removeRoleBtn;
  private JButton saveUserBtn;
  private JButton cancelBtn;
  private JTextField userName;
  private JPasswordField userPassword;
  private JScrollPane userScroller;
  private JScrollPane userRoleScroller;
  private JScrollPane roleScroller;
  private JLabel userRoleListLabel;
  private JLabel roleListLabel;
  private JPanel roleAssociationPanel;

  private UserStore store;
  private boolean isNewUser = false;

  /**
   * Create a new object of class UserPanel
   * @param bundle  provider of string resources
   */
  public UserPanel(ResourceBundle bundle) {
    super();
    buildUi(bundle);
  }

  /**
   * Set the user store for user data edited in this panel
   * @param store  the user store
   */
  public void setUserStore(UserStore store) {
    this.store = store;
    updateUserList();
    updateRoleList(null);
  }

  /**
   * Update the list of users
   */
  private void updateUserList() {
    if (store != null) {
      try {
        userList.removeListSelectionListener(this);
        userList = new JList(store.getUsers().toArray());
        userList.addListSelectionListener(this);
        userScroller.setViewportView(userList);
      } catch (Exception ex) {
        ServiceHub.logStackTrace(ex, getClass());
      }
    }
  }

  /**
   * Update the list of roles
   * @param userId  id of user to show unassigned roles for, show all roles, when null
   */
  private void updateRoleList(Object userId) {
    if (store != null) {
      try {
        roleList.removeListSelectionListener(this);
        if(userId == null) {
          roleList = new JList(store.getRoles().toArray());
        } else {
          roleList = new JList(store.getUnassignedRoles(userId).toArray());
        }
        //roleList.setEnabled(false);
        roleList.addListSelectionListener(this);
        roleScroller.setViewportView(roleList);
       } catch (Exception ex) {
        ServiceHub.logStackTrace(ex, getClass());
      }
    }
  }

  /**
   * Udpate the list of roles associated to a user
   * @param userId  id of user to show roles for or null to clear list
   */
  private void updateUserRoleList(Object userId) {
    if (store != null) {
      try {
        userRoleList.removeListSelectionListener(this);
        if(userId == null) {
          userRoleList = new JList();
        } else {
          userRoleList = new JList(store.getUserRoles(userId).toArray());
          userRoleList.addListSelectionListener(this);
        }
        userRoleScroller.setViewportView(userRoleList);
       } catch (Exception ex) {
        ServiceHub.logStackTrace(ex, getClass());
      }
    }
  }

  /**
   * Create the user interface
   * @param bundle  provider of string resources
   */
  private void buildUi(ResourceBundle bundle) {

    // ---- user list panel ----

    userList = new JList();
    userList.addListSelectionListener(this);
    userScroller = new JScrollPane(userList);

    newUserBtn = new JButton(bundle.getString(RN_USER_PANEL_NEW_BUTTON_LABEL));
    newUserBtn.addActionListener(this);
    delUserBtn = new JButton(bundle.getString(RN_USER_PANEL_DELETE_BUTTON_LABEL));
    delUserBtn.addActionListener(this);
    delUserBtn.setEnabled(false);
    JPanel buttonPanel = new JPanel();
    buttonPanel.add(newUserBtn);
    buttonPanel.add(delUserBtn);

    JPanel userListPanel = new JPanel(new BorderLayout());
    userListPanel.add(userScroller, BorderLayout.CENTER);
    userListPanel.add(buttonPanel, BorderLayout.SOUTH);

    // ---- user panel ----

    GridBagHelper gbh = new GridBagHelper();
    GridBagLayout g = new GridBagLayout();
    GridBagConstraints c = new GridBagConstraints();

    JPanel userPanel = new JPanel(g);
 
    JLabel lb = new JLabel(bundle.getString(RN_USER_PANEL_USER_NAME_LABEL));
    gbh.addGridBagComponent(userPanel, lb, g, c, 0, 0, GridBagConstraints.EAST);

    userName = new JTextField();
    userName.setEnabled(false);
    gbh.addGridBagComponent(userPanel, userName, g, c, 1, 0, GridBagConstraints.WEST,
            1, 1, GridBagConstraints.HORIZONTAL, 1, 1);

    lb = new JLabel(bundle.getString(RN_USER_PANEL_USER_PASSWORD_LABEL));
    gbh.addGridBagComponent(userPanel, lb, g, c, 0, 1, GridBagConstraints.EAST);

    userPassword = new JPasswordField();
    userPassword.setEnabled(false);
    gbh.addGridBagComponent(userPanel, userPassword, g, c, 1, 1, GridBagConstraints.WEST,
            1, 1, GridBagConstraints.HORIZONTAL, 1, 1);

    cancelBtn = new JButton(bundle.getString(RN_USER_PANEL_CANCEL_BUTTON_LABEL));
    cancelBtn.setEnabled(false);
    cancelBtn.addActionListener(this);
    saveUserBtn = new JButton(bundle.getString(RN_USER_PANEL_SAVE_BUTTON_LABEL));
    saveUserBtn.setEnabled(false);
    saveUserBtn.addActionListener(this);
    JPanel saveCancelPanel = new JPanel();
    saveCancelPanel.add(cancelBtn);
    saveCancelPanel.add(saveUserBtn);
    
    gbh.addGridBagComponent(userPanel, saveCancelPanel, g, c, 1, 2, GridBagConstraints.EAST);

    // ---- user to role association panel ----

    userRoleListLabel = new JLabel(bundle.getString(RN_USER_PANEL_USER_ROLE_LIST_LABEL));
    userRoleListLabel.setEnabled(false);
    userRoleList = new JList();
    userRoleList.setEnabled(false);
    userRoleList.addListSelectionListener(this);
    userRoleScroller = new JScrollPane(userRoleList);
    JPanel userRolePanel = new JPanel(new BorderLayout());
    userRolePanel.add(userRoleListLabel, BorderLayout.NORTH);
    userRolePanel.add(userRoleScroller, BorderLayout.CENTER);
    userRolePanel.setMinimumSize(new Dimension(100, 100));
    userRolePanel.setPreferredSize(new Dimension(100, 200));

    addRoleBtn = new JButton(bundle.getString(RN_USER_PANEL_ADD_BUTTON_LABEL));
    addRoleBtn.setEnabled(false);
    addRoleBtn.addActionListener(this);
    removeRoleBtn = new JButton(bundle.getString(RN_USER_PANEL_REMOVE_BUTTON_LABEL));
    removeRoleBtn.setEnabled(false);
    removeRoleBtn.addActionListener(this);
    JPanel userRoleBtnPanel = new JPanel(new GridLayout(2, 1));
    userRoleBtnPanel.add(addRoleBtn);
    userRoleBtnPanel.add(removeRoleBtn);
    JPanel assocCenterPanel = new JPanel(new BorderLayout());
    assocCenterPanel.add(userRoleBtnPanel, BorderLayout.SOUTH);
    assocCenterPanel.setMinimumSize(new Dimension(120, 100));
    assocCenterPanel.setPreferredSize(new Dimension(100, 200));
    //assocCenterPanel.setMaximumSize(new Dimension(100, 100));

    roleListLabel = new JLabel(bundle.getString(RN_USER_PANEL_ROLE_LIST_LABEL));
    roleListLabel.setEnabled(false);
    roleList = new JList();
    roleList.setEnabled(false);
    roleList.addListSelectionListener(this);
    roleScroller = new JScrollPane(roleList);
    JPanel rolePanel = new JPanel(new BorderLayout());
    rolePanel.add(roleListLabel, BorderLayout.NORTH);
    rolePanel.add(roleScroller, BorderLayout.CENTER);
    rolePanel.setMinimumSize(new Dimension(100, 100));
    rolePanel.setPreferredSize(new Dimension(100, 200));

    GridBagLayout g2 = new GridBagLayout();

    roleAssociationPanel = new JPanel(g2);
    roleAssociationPanel.setVisible(false);

    gbh.addGridBagComponent(roleAssociationPanel, userRolePanel, g2, c, 0, 0, GridBagConstraints.CENTER,
      1, 1, GridBagConstraints.VERTICAL, 1, 1);
    gbh.addGridBagComponent(roleAssociationPanel, assocCenterPanel, g2, c, 1, 0, GridBagConstraints.CENTER,
      1, 1, GridBagConstraints.VERTICAL, 1, 1);
    gbh.addGridBagComponent(roleAssociationPanel, rolePanel, g2, c, 2, 0, GridBagConstraints.CENTER,
      1, 1, GridBagConstraints.VERTICAL, 1, 1);

    gbh.addGridBagComponent(userPanel, roleAssociationPanel, g, c, 1, 3, GridBagConstraints.NORTHWEST,
      1, 1, GridBagConstraints.BOTH, 1, 1);

    JPanel centerPanel = new JPanel(new BorderLayout());
    centerPanel.add(userPanel, BorderLayout.NORTH);
    centerPanel.setBorder(new EmptyBorder(12, 12, 12, 12));
    
    setLayout(new BorderLayout());

    add(userListPanel, BorderLayout.WEST);
    add(centerPanel, BorderLayout.CENTER);

    setBorder(new EmptyBorder(12, 12, 12, 12));
  }

  /**
   * Prepare the user interface for editing, i.e. enable controls
   */
  private void startUserEdit() {
    userName.setEnabled(true);
    userPassword.setEnabled(true);
    saveUserBtn.setEnabled(true);
    cancelBtn.setEnabled(true);
    roleList.setEnabled(!isNewUser);

    userRoleListLabel.setEnabled(!isNewUser);
    userRoleList.setEnabled(!isNewUser);
    roleListLabel.setEnabled(!isNewUser);
    roleAssociationPanel.setVisible(!isNewUser);

    userName.requestFocus();
  }

  /**
   * Set the user interface to terminate editing, i.e. disable controls
   */
  private void endUserEdit() {
    cancelBtn.setEnabled(false);
    userName.setEnabled(false);
    userPassword.setEnabled(false);
    saveUserBtn.setEnabled(false);
    userRoleListLabel.setEnabled(false);
    userRoleList.setEnabled(false);
    roleListLabel.setEnabled(false);
    roleList.setEnabled(false);
    addRoleBtn.setEnabled(false);
    removeRoleBtn.setEnabled(false);
    roleAssociationPanel.setVisible(false);
    userName.setText("");
    userPassword.setText("");
    userList.clearSelection();
  }

  /**
   * Delete a user
   */
  private void delUser() {
    Object selectedValue = userList.getSelectedValue();
    if(selectedValue != null) {
      //String userId = selectedValue.toString();
      User user = (User) selectedValue;
      try {
        store.deleteUser(user.getUserId());
        updateUserList();
      } catch (Exception ex) {
        ServiceHub.logStackTrace(ex, getClass());
      }
    }
    endUserEdit();
  }

  /**
   * Add a role to the currently selected user
   */
  private void addRole() {
    Object selectedValue = roleList.getSelectedValue();
    if (selectedValue != null) {
      try {
        //String userId = userList.getSelectedValue().toString();
        User user = (User) userList.getSelectedValue();
        store.addUserRole(user.getUserId(), ((Role) selectedValue).getRoleId());
        updateRoleList(user.getUserId());
        updateUserRoleList(user.getUserId());
        addRoleBtn.setEnabled(false);
      } catch (Exception ex) {
        ServiceHub.logStackTrace(ex, getClass());
      }
    }
  }

  /**
   * Remove a role from the currently seleced user
   */
  private void removeRole() {
    Object o = userRoleList.getSelectedValue();
    if(o != null && o instanceof Role) {
      Role role = (Role) o;
      o = userList.getSelectedValue();
      if(o != null && o instanceof User) {
        User user = (User) o;
        try {
          //String userId = user.getUserName();
          store.removeUserRole(user.getUserId(), role.getRoleId());
          updateRoleList(user.getUserId());
          updateUserRoleList(user.getUserId());
          removeRoleBtn.setEnabled(false);
        } catch (Exception ex) {
          ServiceHub.logStackTrace(ex, getClass());
        }
      }
    }
  }

  /**
   * Save the user that is currently being edited on this panel
   */
  private void saveUser() {
    User user = new ServiceHubUser();
    user.setUserName(userName.getText());
    user.setUserPassword(new String(userPassword.getPassword()));
    if (isNewUser) {
      try {
        store.createUser(user);
      } catch (Exception ex) {
        ServiceHub.logStackTrace(ex, getClass());
      }
    } else {
      Object o = userList.getSelectedValue();
      if (o != null && o instanceof User) {
        User oldUser = (User) o;
        try {
          store.storeUser(oldUser.getUserId(), user);
        } catch (Exception ex) {
          ServiceHub.logStackTrace(ex, getClass());
        }
      }
    }
    isNewUser = false;
    endUserEdit();
    updateUserList();
  }

  /* ------------------ ActionListener ----------------------- */

  /**
   * Handle button actions
   * @param e  the event that triggered this handler
   */
  public void actionPerformed(ActionEvent e) {
    Object src = e.getSource();
    if (src.equals(newUserBtn)) {
      isNewUser = true;
      startUserEdit();
    } else if (src.equals(delUserBtn)) {
      delUser();
    } else if (src.equals(addRoleBtn)) {
      addRole();
    } else if (src.equals(removeRoleBtn)) {
      removeRole();
    } else if (src.equals(saveUserBtn)) {
      saveUser();
    } else if(src.equals(cancelBtn)) {
      isNewUser = false;
      endUserEdit();
    }
  }

  /* ----------------- ListSelectionListener ----------------- */

  /**
   * Handle list selections from this panel
   * @param e  the event that triggered this event
   */
  public void valueChanged(ListSelectionEvent e) {
    Object src = e.getSource();
    if (src.equals(userList)) {
      Object selectedValue = userList.getSelectedValue();
      if(selectedValue != null) {
        User user = (User) selectedValue;
        //String userId = user.getUserName();
        isNewUser = false;
        delUserBtn.setEnabled(true);
        userName.setText(user.getUserName());
        userPassword.setText(user.getUserPassword());
        startUserEdit();
        updateRoleList(user.getUserId());
        updateUserRoleList(user.getUserId());
        roleAssociationPanel.setVisible(true);
      } else {
        delUserBtn.setEnabled(false);
        updateRoleList(null);
        updateUserRoleList(null);
      }
    } else if (src.equals(userRoleList)) {
      Object selectedValue = userRoleList.getSelectedValue();
      if(selectedValue != null) {
        removeRoleBtn.setEnabled(true);
      } else {
        removeRoleBtn.setEnabled(false);
      }
    } else if (src.equals(roleList)) {
      Object selectedValue = roleList.getSelectedValue();
      if(selectedValue != null) {
        addRoleBtn.setEnabled(true);
      } else {
        addRoleBtn.setEnabled(false);
      }
    }
  }

  /**
   * Handle a change of tabs from the main panel, i.e. update role list
   * @param e  the event that triggered this handler
   */
  public void stateChanged(ChangeEvent e) {
    Object src = e.getSource();
    if(src != null && src instanceof MainPanel) {
      MainPanel mainPanel = (MainPanel) src;
      int selectedIndex = mainPanel.getSelectedIndex();
      if(selectedIndex == MainPanel.TAB_USERS) {
        updateRoleList(null);
      }
    }
  }
}
